/* kernel.cpp - David Young */
#include "kernel.h"
void* __cdecl ::operator new (unsigned int size) { return 0; }
void* __cdecl operator new[] (unsigned int size) { return 0; }
void __cdecl ::operator delete (void * p) {}
void __cdecl operator delete[] (void * p) { }
int __cdecl ::_purecall() { for (;;); return 0; }

//Multiboot entry as specified by the multiboot specification.
__declspec(naked) void __multiboot_entry__(void)
{
          __asm {
          
          multiboot_header:
                    dd(0x1BADB002)               ; magic
                    dd(1 << 16)                    ; flags
                    dd(-(0x1BADB002 + (1 << 16)))     ; checksum
                    dd(0x00101000)               ; header_addr
                    dd(0x00101000)               ; load_addr
                    dd(0x0010200F)               ; load_end_addr
                    dd(0x0010200F)               ; bss_end_addr
                    dd(0x00101030)               ; entry_addr
                    dd(0x00000000)               ; mode_type
                    dd(0x00000000)               ; width
                    dd(0x00000000)               ; height
                    dd(0x00000000)               ; depth

          kernel_entry:
                    mov     esp,     KERNEL_STACK

                    xor     ecx,     ecx
                    push     ecx
                    popf

                    push     ebx
					push     eax
                    call     kmain

                    jmp     $
          }
}

/* Setup a descriptor in the Global Descriptor Table */
void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran)
{
    /* Setup the descriptor base address */
    gdt[num].base_low = (base & 0xFFFF);
    gdt[num].base_middle = (base >> 16) & 0xFF;
    gdt[num].base_high = (base >> 24) & 0xFF;

    /* Setup the descriptor limits */
    gdt[num].limit_low = (limit & 0xFFFF);
    gdt[num].granularity = ((limit >> 16) & 0x0F);

    /* Finally, set up the granularity and access flags */
    gdt[num].granularity |= (gran & 0xF0);
    gdt[num].access = access;
}

//Installs GDT and sets data segments to apropriate descriptor
void gdt_install()
{
    gp.limit = (sizeof(struct gdt_entry) * 3) - 1;

    gdt_set_gate(0, 0, 0, 0, 0);
    gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
    gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
	
	gdt_flush();
}

//KERNEL ENTRY
void kmain(unsigned long magic, unsigned long addr)
{
	mbinfo = (multiboot_info *)&addr;
	gdt_install();
	init_vid();
	
}

//Initialize vinfo structure
void init_vid()
{
	vinfo.vmode = 7;
	vinfo.screen_width = 80;
	vinfo.screen_height = 25;
	vinfo.curr_locx = 0;
	vinfo.curr_locy = 0;
}

//Prints out. No write protection.
void kprint(int x, int y, char lett, char attrib)
{
	char* write_loc = (char*)VIDEOMEM + x + (y * vinfo.screen_width);
	*write_loc = lett;
	*(write_loc++) = attrib;
}

//Some write protection. Takes and writes a string utilizing the WHITE character attribute.
void kprint(char* string){
	while(*string)
	{
		if(vinfo.curr_locx >= vinfo.screen_width)
		{
			vinfo.curr_locx = 0;
			vinfo.curr_locy++;
		}
		
		if(vinfo.curr_locy >= vinfo.screen_height)
		{
			kclear();
			vinfo.curr_locy = 0;
		}
		kprint(vinfo.curr_locx, vinfo.curr_locy, *string, (char)WHITE);
		string++;
	}
}

void kclear()
{
	for(int x = 0; x < vinfo.screen_width; x++)
	{
		for(int y = 0; y < vinfo.screen_height; y++)
		{
			kprint(x, y, ' ', (char)BLACK);
		}
	}
}
